package com.briup.pai.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.briup.pai.common.constant.ModelConstant;
import com.briup.pai.common.enums.ModelStatusEnum;
import com.briup.pai.common.enums.ResultCodeEnum;
import com.briup.pai.common.enums.TrainingStatusEnum;
import com.briup.pai.common.exception.BriupAssert;
import com.briup.pai.common.utils.SecurityUtil;
import com.briup.pai.convert.DatasetConvert;
import com.briup.pai.convert.EvaluateLabelConvert;
import com.briup.pai.convert.ModelConvert;
import com.briup.pai.convert.TrainingLabelConvert;
import com.briup.pai.dao.ModelMapper;
import com.briup.pai.entity.dto.ModelSaveDTO;
import com.briup.pai.entity.po.*;
import com.briup.pai.entity.vo.*;
import com.briup.pai.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@CacheConfig(cacheNames = ModelConstant.MODEL_CACHE_PREFIX)
public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements IModelService {

    @Autowired
    private ITrainingService trainingService;
    @Autowired
    private ITrainingDatasetService trainingDatasetService;
    @Autowired
    private ITrainingLabelService trainingLabelService;
    @Autowired
    @Lazy // 解决验证过程中的循环依赖问题
    private IDatasetService datasetService;
    @Autowired
    private IEvaluateService evaluateService;
    @Autowired
    private IEvaluateErrorService evaluateErrorService;
    @Autowired
    private IEvaluateLabelService evaluateLabelService;


    @Autowired
    private ModelConvert modelConvert;
    @Autowired
    private TrainingLabelConvert trainingLabelConvert;
    @Autowired
    private EvaluateLabelConvert evaluateLabelConvert;
    @Autowired
    private DatasetConvert datasetConvert;

    @Override
    public PageVO<ModelPageVO> getModelByPageAndCondition(Integer pageNum, Integer modelType) {
        // 定义分页（pageSize固定为8）
        IPage<Model> page = new Page<>(pageNum, ModelConstant.MODEL_PAGE_SIZE);
        LambdaQueryWrapper<Model> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ObjectUtil.isNotNull(modelType),Model::getModelType, modelType)
                .orderByDesc(Model::getCreateTime);
        // 查询
        List<Model> models = this.list(page, lqw);
        // 转换成ModelPageVO对象
        List<ModelPageVO> modelPageVOS = modelConvert.po2ModelPageVOList(models);
        // 构建返回值
        PageVO<ModelPageVO> result =new PageVO<>();
        result.setData(modelPageVOS);
        result.setTotal(page.getTotal());
        System.out.println(result.getTotal());
        System.out.println(page.getTotal());
        return result;
    }

    @Override
    @Cacheable(key = "#modelId")
    public ModelEchoVO getModelById(Integer modelId) {
        return modelConvert.po2ModelEchoVO(
                BriupAssert.requireNotNull(
                        this,
                        Model::getId,
                        modelId,
                        ResultCodeEnum.DATA_NOT_EXIST
                ));
    }

    @Override
    @CachePut(key = "#result.modelId")
    public ModelEchoVO addAndModifyModel(ModelSaveDTO modelSaveDTO) {
        // 判断是添加还是修改
        Integer modelId = modelSaveDTO.getModelId();
        Model model;
        if (ObjectUtil.isNull(modelId)) {
            // 验证名字不能重复
            BriupAssert.requireNull(
                    this,
                    Model::getModelName,
                    modelSaveDTO.getModelName(),
                    ResultCodeEnum.DATA_ALREADY_EXIST);
            // 添加，需要手动定义一些初始值
            model = modelConvert.modelSaveDTO2Po(modelSaveDTO);
            model.setModelStatus(ModelStatusEnum.Unpublished.getStatus());
            model.setAccuracyRate(0.0);
            model.setLastModelVersion(0);
            model.setTrainingStatus(TrainingStatusEnum.NO_TRAINING.getStatus());
            model.setCreateBy(SecurityUtil.getUserId());
            this.save(model);
        } else {
            // 模型必须存在
            Model temp = BriupAssert.requireNotNull(
                    this,
                    Model::getId,
                    modelId,
                    ResultCodeEnum.DATA_NOT_EXIST);
            // 模型名称不能重复
            BriupAssert.requireNull(
                    this,
                    Model::getModelName,
                    modelSaveDTO.getModelName(),
                    Model::getId,
                    temp.getId(),
                    ResultCodeEnum.DATA_ALREADY_EXIST
            );
            // 模型分类不能修改
            BriupAssert.requireEqual(
                    temp.getModelType(),
                    modelSaveDTO.getModelType(),
                    ResultCodeEnum.PARAM_IS_ERROR);
            // 修改
            model = modelConvert.modelSaveDTO2Po(modelSaveDTO);
            this.updateById(model);
        }

        return modelConvert.po2ModelEchoVO(model);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(key = "#modelId"),
            @CacheEvict(key = "T(com.briup.pai.common.constant.CommonConstant).DETAIL_CACHE_PREFIX+':'+#modelId")
    })
    public void removeModelById(Integer modelId) {
        Model temp = BriupAssert.requireNotNull(
                this,
                Model::getId,
                modelId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        // 要求模型的训练状态为未训练
        BriupAssert.requireEqual(
                temp.getTrainingStatus(),
                TrainingStatusEnum.NO_TRAINING.getStatus(),
                ResultCodeEnum.DATA_CAN_NOT_DELETE
        );
        this.removeById(modelId);
    }

    @Override
    @Cacheable(key = "T(com.briup.pai.common.constant.CommonConstant).DETAIL_CACHE_PREFIX+':'+#modelId")
    public ModelDetailVO getModelDetailById(Integer modelId) {
        // 模型必须存在
        Model model = BriupAssert.requireNotNull(
                this,
                Model::getId,
                modelId,
                ResultCodeEnum.DATA_NOT_EXIST
        );
        // 转换成vo
        ModelDetailVO modelDetailVO = modelConvert.po2ModelDetailVO(model);
        // 查询模型训练的历史
        if (ObjectUtil.notEqual(model.getTrainingStatus(), TrainingStatusEnum.NO_TRAINING.getStatus())) {
            LambdaQueryWrapper<Training> trainingWrapper = new LambdaQueryWrapper<>();
            trainingWrapper.eq(Training::getModelId, modelId);
            List<ModelHistoryVO> results = trainingService.list(trainingWrapper)
                    .stream()
                    .map(training -> {
                        // 查询训练所使用的数据集名称
                        List<String> datasetNames = datasetConvert.getDatasetNames(training.getId());
                        ModelHistoryVO modelHistoryVO = new ModelHistoryVO();
                        modelHistoryVO.setModelVersion(training.getModelVersion());
                        modelHistoryVO.setDatasetNames(datasetNames);
                        modelHistoryVO.setAccuracyRate(training.getAccuracyRate());
                        modelHistoryVO.setTrainDate(training.getCreateTime());

                        // 查询训练结果
                        LambdaQueryWrapper<TrainingLabel> trainingLabelWrapper = new LambdaQueryWrapper<>();
                        trainingLabelWrapper.eq(TrainingLabel::getTrainingId, training.getId());
                        List<ModelOperationResultVO> trainingResults = trainingLabelConvert.po2ModelOperationResultVOList(trainingLabelService.list(trainingLabelWrapper));
                        modelHistoryVO.setTrainResults(trainingResults);

                        // 查询评估结果
                        LambdaQueryWrapper<Evaluate> evaluateWrapper = new LambdaQueryWrapper<>();
                        evaluateWrapper.eq(Evaluate::getTrainingId, training.getId());
                        List<Evaluate> list = evaluateService.list(evaluateWrapper);
                        if (ObjectUtil.isNotEmpty(list)) {
                            List<ModelEvaluateReportVO> modelEvaluateReportVOS = list.stream()
                                    .map(evaluate -> {
                                        // 封装评估的结果
                                        ModelEvaluateReportVO modelEvaluateReportVO = new ModelEvaluateReportVO();
                                        // 封装数据集名称、评估准确率
                                        modelEvaluateReportVO.setDatasetName(datasetService.getById(evaluate.getDatasetId()).getDatasetName());
                                        modelEvaluateReportVO.setAccuracyRate(evaluate.getAccuracyRate());
                                        // 封装评估错误数量
                                        Integer evaluateId = evaluate.getId();
                                        LambdaQueryWrapper<EvaluateError> evaluateErrorWrapper = new LambdaQueryWrapper<>();
                                        evaluateErrorWrapper.eq(EvaluateError::getEvaluateId, evaluateId);
                                        modelEvaluateReportVO.setErrorCount(evaluateErrorService.count(evaluateErrorWrapper));
                                        // 封装评估结果
                                        LambdaQueryWrapper<EvaluateLabel> evaluateLabelWrapper = new LambdaQueryWrapper<>();
                                        evaluateLabelWrapper.eq(EvaluateLabel::getEvaluateId, evaluateId);
                                        List<ModelOperationResultVO> evaluateResults = evaluateLabelConvert.po2ModelOperationResultVOList(evaluateLabelService.list(evaluateLabelWrapper));
                                        modelEvaluateReportVO.setEvaluateResults(evaluateResults);
                                        return modelEvaluateReportVO;
                                    })
                                    .toList();
                            modelHistoryVO.setEvaluateReport(modelEvaluateReportVOS);
                        }
                        return modelHistoryVO;
                    })
                    .toList();
            modelDetailVO.setTrainingHistory(results);
        }
        return modelDetailVO;
    }

    @Override
    public List<Integer> getDatasetIdsUsed() {
        // 获取被训练过或者评估过的数据集ids
        // 准备一个集合返回
        List<Integer> datasetIds = new ArrayList<>();
        // 查询所有训练使用的数据集
        List<Integer> l1 = trainingDatasetService.list().stream().map(TrainingDataset::getDatasetId).toList();
        // 查询所有评估的数据集
        List<Integer> l2 = evaluateService.list().stream().map(Evaluate::getDatasetId).toList();
        // 合并
        datasetIds.addAll(l1);
        datasetIds.addAll(l2);
        return datasetIds;
    }
}